package com.tsyz.servlet.interceptor;

import com.tsyz.servlet.util.TraceIdUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 基础拦截器抽象类，提供公共功能
 */
@Slf4j
public abstract class BaseInterceptor implements HandlerInterceptor {

    // 使用ThreadLocal存储请求开始时间，确保线程安全
    private static final ThreadLocal<Long> startTimeThreadLocal = new ThreadLocal<>();

    /**
     * 预处理方法，在请求处理之前执行
     *
     * @param request  HTTP请求对象
     * @param response HTTP响应对象
     * @param handler  处理器对象
     * @return boolean 是否继续执行后续处理
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 记录请求开始时间
        long startTime = System.currentTimeMillis();
        startTimeThreadLocal.set(startTime);

        // 确保有TraceId
        ensureTraceId();

        // 执行子类特定的预处理逻辑
        return doPreHandle(request, response, handler);
    }

    /**
     * 后处理方法，在请求处理完成后、视图渲染之前执行
     *
     * @param request      HTTP请求对象
     * @param response     HTTP响应对象
     * @param handler      处理器对象
     * @param modelAndView 模型和视图对象
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 执行子类特定的后处理逻辑
        doPostHandle(request, response, handler, modelAndView);
    }

    /**
     * 请求完成后的回调方法，在请求完全结束后执行
     *
     * @param request  HTTP请求对象
     * @param response HTTP响应对象
     * @param handler  处理器对象
     * @param ex       处理过程中抛出的异常
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 记录异常信息
        if (ex != null) {
            String traceId = MDC.get(TraceIdUtil.TRACE_ID);
            log.error("[traceId:{}] 请求处理异常: {}: {}", traceId, ex.getClass().getSimpleName(), ex.getMessage(), ex);
        }

        // 清理ThreadLocal，防止内存泄漏
        startTimeThreadLocal.remove();

        // 执行子类特定的完成处理逻辑
        doAfterCompletion(request, response, handler, ex);
    }

    /**
     * 子类实现的预处理逻辑
     *
     * @param request  HTTP请求对象
     * @param response HTTP响应对象
     * @param handler  处理器对象
     * @return boolean 是否继续执行后续处理
     */
    protected abstract boolean doPreHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    /**
     * 子类实现的后处理逻辑
     *
     * @param request      HTTP请求对象
     * @param response     HTTP响应对象
     * @param handler      处理器对象
     * @param modelAndView 模型和视图对象
     */
    protected abstract void doPostHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;

    /**
     * 子类实现的完成处理逻辑
     *
     * @param request  HTTP请求对象
     * @param response HTTP响应对象
     * @param handler  处理器对象
     * @param ex       处理过程中抛出的异常
     */
    protected abstract void doAfterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;

    /**
     * 确保当前线程有有效的TraceId
     * 对于静态资源等不需要traceId的请求，不产生警告日志
     */
    private void ensureTraceId() {
        String traceId = MDC.get(TraceIdUtil.TRACE_ID);
        // 不再检查traceId是否存在，避免对静态资源产生不必要的警告日志
    }

    /**
     * 记录请求处理耗时 - 供子类调用
     */
    protected void logProcessingTime() {
        Long startTime = startTimeThreadLocal.get();
        if (startTime != null) {
            long duration = System.currentTimeMillis() - startTime;
            String traceId = MDC.get(TraceIdUtil.TRACE_ID);
            log.info("[traceId:{}] >>> 请求处理完成，耗时: {}ms <<<", traceId, duration);
        }
    }

    /**
     * 记录请求总耗时 - 供子类调用
     */
    protected void logCompletionTime() {
        Long startTime = startTimeThreadLocal.get();
        if (startTime != null) {
            long duration = System.currentTimeMillis() - startTime;
            String traceId = MDC.get(TraceIdUtil.TRACE_ID);
            log.info("[traceId:{}] >>> 请求完全结束，总耗时: {}ms <<<", traceId, duration);
        }
    }
}
